package thread;

import java.util.List;
import java.util.Random;
import java.util.Spliterator;
import java.util.concurrent.*;
import java.util.function.Consumer;

/**
 * Created by User on 2017/10/12.
 */
public class TestCopyOnWriteArrayList {

    static CountDownLatch needStartCount = new CountDownLatch(10);


    static CountDownLatch needEndCount = new CountDownLatch(10);

    public static void main(String[] args) throws InterruptedException, ExecutionException {

        List list = new CopyOnWriteArrayList();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }




        Spliterator spliterator = list.spliterator();

        ForkJoinPool pool = new ForkJoinPool();

        ForkJoinTask<Integer> submit = pool.submit(new CountTask(spliterator));

        Integer result = submit.get();

        System.out.println(result);



        if (true) return;

        new ForeachThread(list).start();
        new ForeachThread(list).start();

        new OperationThread(list).start();
        new OperationThread(list).start();
        new OperationThread(list).start();
        new OperationThread(list).start();
        new OperationThread(list).start();
        new OperationThread(list).start();
        new OperationThread(list).start();
        new OperationThread(list).start();

        needEndCount.await();
        for (Object obj : list) System.out.println(Thread.currentThread().getId() + ":" + obj);

    }


    static class ForeachThread extends Thread {

        List list;

        public ForeachThread(List list) {
            this.list = list;
        }

        @Override
        public void run() {
            try {
                needStartCount.countDown();
                needStartCount.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (Object obj : list) System.out.println(Thread.currentThread().getId() + ":" + obj);

            needEndCount.countDown();
        }
    }


    static class OperationThread extends Thread {
        List list;

        public OperationThread(List list) {
            this.list = list;
        }

        @Override
        public void run() {
            try {
                needStartCount.countDown();
                needStartCount.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Random random = new Random();
            int i = random.nextInt(list.size());
            list.remove(i);

            needEndCount.countDown();
        }
    }


    static class CountTask extends RecursiveTask<Integer> {

        static final int threshold = 2;

        Spliterator<Integer> spliterator;

        public CountTask(Spliterator spliterator) {
            this.spliterator = spliterator;
        }

        Boolean canDoTask() {
            return spliterator.estimateSize() <= threshold;
        }

        Integer doTask() {
            MathAddConsumer consumer = new MathAddConsumer();

            spliterator.forEachRemaining(consumer);

            return consumer.getResult();
        }

        @Override
        protected Integer compute() {
            if (!canDoTask()) {
                Spliterator<Integer> spliterator1 = spliterator;
                Spliterator<Integer> spliterator2 = spliterator1.trySplit();

                CountTask countTask1 = new CountTask(spliterator1);
                CountTask countTask2 = new CountTask(spliterator2);

                ForkJoinTask<Integer> fork1 = countTask1.fork();
                ForkJoinTask<Integer> fork2 = countTask2.fork();

                Integer join1 = fork1.join();
                Integer join2 = fork2.join();

                return join1 + join2;
            } else {
                return doTask();
            }
        }


    }

    static class MathAddConsumer implements Consumer<Integer> {

        public Integer result = 0;

        @Override
        public void accept(Integer integer) {
            result += integer;
        }

        public Integer getResult() {
            return result;
        }
    }

}
